package com.tbit.main.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tbit.common.constant.OperationPlatform;
import com.tbit.common.entity.MachineOpLog;
import com.tbit.main.constant.*;
import com.tbit.main.controller.interceptor.LoginUserContextHolder;
import com.tbit.main.exception.BaseException;
import com.tbit.main.pojo.*;
import com.tbit.main.pojo.VO.TerControlReqVO;
import com.tbit.main.pojo.bo.SendControlBO;
import com.tbit.main.pojo.bo.CommonControlParamBO;
import com.tbit.main.pojo.bo.TerControlBO;
import com.tbit.main.pojo.temppower.TempPowerMsg;
import com.tbit.main.service.*;
import com.tbit.main.service.ter.TerControlFailType;
import com.tbit.main.service.ter.TerControlResult;
import com.tbit.main.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tbit.main.constant.RedisConstant.PHOTO;
import static com.tbit.main.constant.RedisConstant.REDIS_TER_BIKE_PHOTO;

/**
 * @author Leon
 */
@Slf4j
@Service
public class TerControlServiceImpl implements TerControlService {

    @Autowired
    private MQProducer mqProducer;
    @Autowired
    private RedisService redisService;
    @Autowired
    private MachineService machineService;
    @Autowired
    private MachineStatusService machineStatusService;
    @Autowired
    private AccountConfigService accountConfigService;
    @Autowired
    private OrderLogService orderLogService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private MachineLoseService machineLoseService;
    @Autowired
    private DataService dataService;
    @Autowired
    private MachineOccupyService machineOccupyService;
    @Autowired
    private TerControlService terControlService;
    @Autowired
    private AccountUserService accountUserService;
    @Autowired
    private MachineFunctionService machineFunctionService;
    @Autowired
    private MachineOpLogService machineOpLogService;
    @Autowired
    private PowerChangePerformanceService powerChangePerformanceService;

    @Value("${mq.batterChange}")
    private Boolean flag;

    @Override
    public void sendControl(String serNO, String machineNO, String controlType, String paramName, String paramValue) {
        /**查询消息是在的队列名称*/
        String routeKey = redisService.getGetWay(RedisUtil.getTerHashId(machineNO), RedisConstant.REDIS_TER_ROUTE_KEY);

        if (StrUtil.isBlank(routeKey)) {
            log.warn("[{}]远程指令无路由信息", machineNO);
            return;
        }

        /**封装消息*/
        TerMsg termsg = new TerMsg();
        termsg.setSn(machineNO);
        termsg.setSerNO(serNO);
        termsg.setControlType(controlType);
        termsg.setParamName(paramName);
        termsg.setParamValue(paramValue);

        TerControlMsg<TerMsg> terControlMsg = new TerControlMsg<>();
        terControlMsg.setFeedback("tbit_uqbike_web_main");
        terControlMsg.setMsgId(GatewayProtocol.MQ_MSG_ID_CONTROL_TER);
        terControlMsg.setData(termsg);

        /**添加到内存中*/
        redisService.add(RedisConstant.terControlMsg + serNO, JSONObject.toJSONString(terControlMsg), 15);

        /**发送到MQ*/
        mqProducer.sendDataToQueue(routeKey, JSON.toJSONString(terControlMsg));

        // 插入车辆操作日志
        if (ControlTypeConstant.CONTROL_TYPE_CONTROL.equals(controlType)) {
            MachineOpLog opLog = getOpLogByType(paramName);
            if (opLog != null) {
                String detail = "指令流水号：" + serNO;
                opLog.setDetail(detail);


                AccountUser accountUser = LoginUserContextHolder.getLoginUser();
                if (accountUser != null) {
                    Machine machine = dataService.getMachine(dataService.getMachineId(machineNO));
                    //mq回调不进行记录，取不到登录用户
                    opLog.setOpUser(accountUser.getName());
                    opLog.setOpUserPhone(accountUser.getPhone());
                    opLog.setResult(true);
                    opLog.setOpTime(LocalDateTime.now());
                    opLog.setAccountId(machine.getAccountId());
                    opLog.setOpPlatform(OperationPlatform.MAINTAIN.code);
                    opLog.setMachineId(machine.getMachineId());
                    opLog.setMachineNO(machine.getMachineNO());
                    opLog.setUserCode(machine.getUserCode());
                    machineOpLogService.insertSingle(opLog);
                }
            }
        }

        /**添加到流水号到内存*/
        redisService.add(RedisConstant.terControl + serNO, "-1", 15);
    }

    private MachineOpLog getOpLogByType(String type) {
        switch (type) {
            case ControlTypeConstant.CONTROL_TYPE_LOCK:
                return MachineOpLog.builder()
                        .operation("车辆锁")
                        .opType("关闭")
                        .build();
            case ControlTypeConstant.CONTROL_TYPE_UNLOCK:
                return MachineOpLog.builder()
                        .operation("车辆锁")
                        .opType("开启")
                        .build();
            case ControlTypeConstant.CONTROL_TYPE_UNSTART:
                return MachineOpLog.builder()
                        .operation("车辆断电")
                        .build();
            case ControlTypeConstant.CONTROL_TYPE_LOCATION:
                return MachineOpLog.builder()
                        .operation("定位")
                        .build();
            case ControlTypeConstant.MACHINE_CONTROL_FIND:
                return MachineOpLog.builder()
                        .operation("寻车")
                        .build();
            case ControlTypeConstant.CONTROL_TYPE_RESTART:
                return MachineOpLog.builder()
                        .operation("重启")
                        .build();
            case ControlTypeConstant.MACHINE_CONTROL_BAT_UNLOCK:
                return MachineOpLog.builder()
                        .operation("电池锁")
                        .opType("打开")
                        .build();
            case ControlTypeConstant.MACHINE_CONTROL_BAT_LOCK:
                return MachineOpLog.builder()
                        .operation("电池锁")
                        .opType("关闭")
                        .build();
            case ControlTypeConstant.MACHINE_CONTROL_OPEN_TK:
                return MachineOpLog.builder()
                        .operation("头盔锁")
                        .opType("打开")
                        .build();
            case ControlTypeConstant.MACHINE_CONTROL_CLOSE_TK:
                return MachineOpLog.builder()
                        .operation("头盔锁")
                        .opType("关闭")
                        .build();
            case ControlTypeConstant.CONTROL_TYPE_FORCE_CLOSE_SMART_HELMET:
                return MachineOpLog.builder()
                        .operation("智能头盔锁")
                        .opType("强制关闭")
                        .build();
            default:
                return null;
        }
    }

    @Override
    public void sendBatchControl(String machineNO, List<TerMsg> terMessages) {
        /**查询消息是在的队列名称*/
        String routeKey = redisService.getGetWay(RedisUtil.getTerHashId(machineNO), RedisConstant.REDIS_TER_ROUTE_KEY);
        if (StrUtil.isBlank(routeKey)) {
            log.warn("[{}]远程指令无路由信息", machineNO);
            return;
        }

        TerControlMsg<List<TerMsg>> terControlMessages = new TerControlMsg<>();
        terControlMessages.setFeedback("tbit_uqbike_web_main");
        terControlMessages.setMsgId(GatewayProtocol.MQ_MSG_ID_CONTROL_TER_BATCH);
        terControlMessages.setData(terMessages);

        mqProducer.sendDataToQueue(routeKey, JSON.toJSONString(terControlMessages));

        for (TerMsg terMessage : terMessages) {
            String serNo = terMessage.getSerNO();
            /**添加到流水号到内存*/
            redisService.add(RedisConstant.terControl + serNo, "-1", 15);
            redisService.add(RedisConstant.terControlMsg + serNo, JSONObject.toJSONString(terControlMessages), 15);
        }
    }

    @Override
    public void sendBatchControl(List<SendControlBO> sendControlBOS) {
        for (SendControlBO sendControlBO : sendControlBOS) {
            String machineNo = sendControlBO.getMachineNo();
            TerMsg terMessage = sendControlBO.getTerMessage();

            String routeKey = redisService.getGetWay(RedisUtil.getTerHashId(machineNo), RedisConstant.REDIS_TER_ROUTE_KEY);
            if (StrUtil.isBlank(routeKey)) {
                log.warn("[{}]远程指令无路由信息", machineNo);
                continue;
            }

            TerControlMsg<TerMsg> terControlMessage = new TerControlMsg<>();
            terControlMessage.setFeedback("tbit_uqbike_web_main");
            terControlMessage.setMsgId(GatewayProtocol.MQ_MSG_ID_CONTROL_TER);
            terControlMessage.setData(terMessage);

            /**添加到内存中*/
            redisService.add(RedisConstant.terControlMsg + terMessage.getSerNO(), JSONObject.toJSONString(terMessage), 15);

            /**发送到MQ*/
            mqProducer.sendDataToQueue(routeKey, JSON.toJSONString(terControlMessage));

            /**添加到流水号到内存*/
            redisService.add(RedisConstant.terControl + terMessage.getSerNO(), "-1", 15);
        }
    }


    @Override
    public void respHandle(String serNO, String controlRet) {
        /**根据流水号查询下发的信息*/
        String serNOControlRet = redisService.get(RedisConstant.terControl + serNO);
        if (serNOControlRet != null) {
            redisService.add(RedisConstant.terControl + serNO, controlRet, 15);
        }
    }

    @Override
    public String control(TerControlReqVO reqVO) {
        AccountUser accountUser = LoginUserContextHolder.getLoginUser();

        /**查询设备信息*/
        Machine machine = machineService.getByUseCode(reqVO.getUserCode());
        if (machine == null) {
            throw new BaseException("Machine.notExist");
        }

        /**触发删除用户检测*/
        AccountUser accountUserDb = accountUserService.getById(accountUser.getAccountUserId());
        if (accountUserDb == null) {
            /**删除token*/
            redisService.del(accountUser.getToken());
            throw new BaseException("AccountUser.notExist");
        }

        /**设备权限判断*/
        machineService.auth(accountUser.getAgents(), machine.getMachineId());

        Double lon = reqVO.getLon();
        Double lat = reqVO.getLat();
        Integer type = reqVO.getType();
        Integer mapType = reqVO.getMapType();
        String machineNO = machine.getMachineNO();

        Integer values = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_OPERATING_DISTANCE);
        if (values != null && values.equals(1)) {

            Point pointUser = new Point(lon, lat);
            pointUser = calibrateService.commonCalibrate(pointUser, mapType, MapConstant.MAP_ORI);
            MachineStatus machines = machineStatusService.getByMachineId(machine.getMachineId());
            if (machines == null) {
                throw new BaseException("MachineState.not.gps");
            }
            double distance = GeoUtil.getDistance(pointUser, new Point(machines.getLon(), machines.getLat()));
            AccountConfig accountConfig = dataService.getAccountConfig(machine.getAccountId());
            if (distance > accountConfig.getTerMileage()) {
                throw new BaseException("terMileage.not");
            }
        }

        MachineLose machineLose = machineLoseService.getAccountIdAndUserCode(machine.getAccountId(), machine.getUserCode());
        if (machineLose != null) {
            throw new BaseException("machine.lose.exit");
        }

        /**生成流水号*/
        String serNO = UUID.randomUUID().toString();

        switch (type) {
            case ControlType.MACHINE_CONTROL_UNLOCK:

                Integer value = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_LOCK_VEHICLE);
                if (value != null && value.equals(1)) {
                    /**获取配置，redis运维占用车辆*/
                    AccountConfig accountConfig = accountConfigService.getByAccountId(accountUser.getAccountId());
                    /**运维占用最大数判断*/
                    if (accountConfig != null) {
                        if (accountConfig.getOccupyMax() != null && !accountConfig.getOccupyMax().equals(0)) {
                            Integer occupy = machineOccupyService.getByAccountUserId(accountUser.getAccountUserId());
                            if (occupy >= accountConfig.getOccupyMax()) {
                                throw new BaseException("Machine.occupyMax.exist");
                            }
                        }
                    }
                }

                commonControl(new CommonControlParamBO(serNO, machine, type, "开锁"));
//                redisService.add(String.format("BackCyclingTimeStart#%s", machine.getMachineNO()), DateTimeUtil.getNowTime(), 600);
                sendTempPowerMq(machineNO, machine.getAccountId());
                break;
            case ControlType.MACHINE_CONTROL_LOCK:
                commonControl(new CommonControlParamBO(serNO, machine, type, "上锁"));
                redisService.del(RedisConstant.REDIS_TER_BIKE_PUSH_MACHINE_USER_CODE + machine.getUserCode());
                break;
            case ControlType.MACHINE_CONTROL_BAT_UNLOCK:

                /**获取最后状态*/
                MachineStatus machineStatus = machineStatusService.getByMachineId(machine.getMachineId());
                if (machineStatus == null) {
                    throw new BaseException("Machine.notActive");
                }

                log.info("开电池锁：{}", machineStatus);

                log.info("[{}]开始换电", machineNO);

                /**换电阈值配置*/
                AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
                Integer changeBattery = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_CHANGE_BATTERY);

                /**普通运维人员不能还低于指定阈值的车辆*/
                if (accountConfig != null && machineStatus.getSocPercent() > accountConfig.getSocLimit()) {
                    log.warn("[{}]运维无效换电", machineNO);
                    if (changeBattery != null && changeBattery.equals(1)) {
                        throw new BaseException("Machine.soc.over");
                    }
                }

                /**是否添加*/
                boolean isAdd = true;
                if (accountConfig != null) {
                    /**普通运维人员必须要在指定电量以下添加才有效*/
                    if (changeBattery != null && changeBattery.equals(1)) {
                        if (machineStatus.getSocPercent() > accountConfig.getSocLimit()) {
                            isAdd = false;
                        }
                    }
                }


                if (isAdd) {
                    /**记录换电记录*/
                    PowerChangeLog pcl = new PowerChangeLog();
                    pcl.setAccountId(machine.getAccountId());
                    pcl.setMachineId(machine.getMachineId());
                    pcl.setUserCode(machine.getUserCode());
                    pcl.setMachineNO(machineNO);
                    pcl.setUserMainId(accountUser.getAccountUserId());
                    pcl.setChangeTime(DateTimeUtil.getNowTime());
                    pcl.setOldSoc(machineStatus.getSocPercent());
                    pcl.setMachineType(machine.getMachineType());
                    pcl.setRemark("换电前电压=" + machineStatus.getBatteryEU() + ";");
                    pcl.setAuditorId(1);
                    pcl.setIsValid(0);
                    pcl.setLat(lat);
                    pcl.setLon(lon);

                    // 运维绩效：记录站点
                    powerChangePerformanceService.cacheParkPoint(machine.getMachineId());

                    /**记录运维事件轨迹*/
                    try {
                        TerPosition terPosition = new TerPosition();
                        terPosition.setMachineNO(accountUser.getPhone());
                        terPosition.setLat(lat);
                        terPosition.setLon(lon);
                        terPosition.setDt(DateTimeUtil.getNowTime());
                        terPosition.setExData(HistoryExData.UW + "=" + HistoryExData.HD + ";");
                        calibrateService.calTerPosition(terPosition, mapType, SysConstant.ORIGINAL);
                        historyService.insert(terPosition);
                    } catch (Exception e) {
                        log.info("错误：{}", e.getMessage(), e);
                    }

                    /**添加到redis*/
                    redisService.add(RedisConstant.REDIS_TER_BATTERY_CHANGE + machineNO, JSONObject.toJSONString(pcl), 1200);


                    if (flag) {
                        /** 获取换电数据，推送MQ*/
                        PowerChangeMQ powerChangeMQ = new PowerChangeMQ();
                        powerChangeMQ.setAccountId(machineStatus.getAccountId());
                        powerChangeMQ.setMachineId(machineStatus.getMachineId());
                        powerChangeMQ.setMachineNO(machineStatus.getMachineNO());
                        powerChangeMQ.setUserCode(machineStatus.getUserCode());
                        powerChangeMQ.setOldVol((double) machineStatus.getBatteryEU());
                        powerChangeMQ.setChangeTime(DateTimeUtil.StringToDateTime(DateTimeUtil.getNowTime()));
                        /** 添加到redis，推送MQ*/
                        redisService.add(RedisConstant.REDIS_TER_BATTERY_CHANGE + "MQ" + machineNO, JSONObject.toJSONString(powerChangeMQ), 1200);
                    }

                    /**添加到redismap*/
                    redisService.set(RedisConstant.REDIS_TER_BIKE_BATTERY_CHANGE_MAP, machineNO, String.valueOf(System.currentTimeMillis()));
                }
                commonControl(new CommonControlParamBO(serNO, machine, type, "开电池锁"));
                break;
            case ControlType.MACHINE_CONTROL_BAT_LOCK:
                commonControl(new CommonControlParamBO(serNO, machine, type, "关电池锁"));
                break;
            case ControlType.MACHINE_CONTROL_FIND:
                commonControl(new CommonControlParamBO(serNO, machine, type, "寻车指令"));
                break;
            case ControlType.MACHINE_CONTROL_DW:
                commonControl(new CommonControlParamBO(serNO, machine, type, "定位指令"));
                break;
            case ControlType.MACHINE_CONTROL_OPEN_TK:
                commonControl(new CommonControlParamBO(serNO, machine, type, "开头盔锁"));
                break;
            case ControlType.MACHINE_CONTROL_CLOSE_TK:
                commonControl(new CommonControlParamBO(serNO, machine, type, "关头盔锁"));
                break;
            case ControlType.MACHINE_CONTROL_COERCE_HRESTE:
                commonControl(new CommonControlParamBO(serNO, machine, type, "氢气复位"));
                break;
            case ControlType.MACHINE_CONTROL_COERCE_LOCK:
                commonControl(new CommonControlParamBO(serNO, machine, type, "强制开锁"));
                sendTempPowerMq(machineNO, machine.getAccountId());
//                redisService.add(String.format("BackCyclingTimeStart#%s", machine.getMachineNO()), DateTimeUtil.getNowTime(), 600);
                break;
            case ControlType.MACHINE_CONTROL_COERCE_UNLOCK:
                commonControl(new CommonControlParamBO(serNO, machine, type, "强制关锁"));
                redisService.del(RedisConstant.REDIS_TER_BIKE_PUSH_MACHINE_USER_CODE + machine.getUserCode());
                break;
            case ControlType.MACHINE_CONTROL_COERCE_PHOTOGRAPH:
                String machineNOPhoto = redisService.get(REDIS_TER_BIKE_PHOTO + machineNO);
                if (machineNOPhoto != null) {
                    throw new BaseException("phone.not.fast");
                } else {
                    redisService.add(REDIS_TER_BIKE_PHOTO + machineNO, machineNO, 10);
                }
                commonControl(new CommonControlParamBO(serNO, machine, type, "设备拍照", reqVO.getPhotograph(), null, reqVO.getCameraType(), null));
                break;
            case ControlType.MACHINE_CONTROL_COERCE_PICTURE:
                commonControl(new CommonControlParamBO(serNO, machine, type, "设备上传图片", null, null, reqVO.getCameraType(), null));
                break;
            case ControlType.MACHINE_CONTROL_COERCE_HELMET:
                commonControl(new CommonControlParamBO(serNO, machine, type, "设备设置头盔锁mac", null, reqVO.getHelmetMac(), reqVO.getCameraType(), null));
                break;
            case ControlType.MACHINE_CONTROL_RESTART:
                commonControl(new CommonControlParamBO(serNO, machine, type, "重启设备"));
                break;
            case ControlType.MACHINE_CONTROL_MANNED:
                commonControl(new CommonControlParamBO(serNO, machine, type, "载人设备", null, null, reqVO.getCameraType(), reqVO.getMannedNO()));
                break;
            case ControlType.MACHINE_CONTROL_PUSH_MACHINE:
                Integer maxUnLockNum = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_LOCK_VEHICLE);
                if (maxUnLockNum != null && maxUnLockNum.equals(1)) {
                    /**获取配置，redis运维占用车辆*/
                    AccountConfig accountConfigById = accountConfigService.getByAccountId(accountUser.getAccountId());
                    /**运维占用最大数判断*/
                    if (accountConfigById != null) {
                        if (accountConfigById.getOccupyMax() != null && !accountConfigById.getOccupyMax().equals(0)) {
                            Integer occupy = machineOccupyService.getByAccountUserId(accountUser.getAccountUserId());
                            if (occupy >= accountConfigById.getOccupyMax()) {
                                throw new BaseException("Machine.occupyMax.exist");
                            }
                        }
                    }
                }
                redisService.add(RedisConstant.REDIS_TER_BIKE_PUSH_MACHINE_USER_CODE + machine.getUserCode(), machine.getUserCode());
                commonControl(new CommonControlParamBO(serNO, machine, type, ControlType.MACHINE_CONTROL_PUSH_MACHINE_N));
                break;
            default:
                throw new BaseException("Machine.order.notDis");
        }

        return serNO;
    }


    @Override
    public void commonControl(CommonControlParamBO controlParam) {
        AccountUser accountUser = LoginUserContextHolder.getLoginUser();
        Machine machine = controlParam.getMachine();

        // 创建消息对象
        TerMsg result = createTerMsg(accountUser, controlParam.getSerNo(), machine, controlParam.getType(),
                controlParam.getPhotograph(), controlParam.getHelmetMac(), controlParam.getCameraType(), controlParam.getManned());

        // 发送
        sendControl(controlParam.getSerNo(), machine.getMachineNO(), result.getControlType(), result.getParamName(), result.getParamValue());

        // 保存指令日志
        saveOrderLog(accountUser, controlParam.getSerNo(), machine, controlParam.getType(), controlParam.getRemark(),
                result.getControlType(), result.getParamName());
    }

    private TerMsg createTerMsg(AccountUser accountUser, String serNO, Machine machine, Integer type, Integer photograph, String helmetMac, Integer cameraType, String manned) {
        String paramName;
        String paramValue = "";
        String orderType;

        String nowTime = DateTimeUtil.getNowTime();
        switch (type) {
            case ControlType.MACHINE_CONTROL_UNLOCK:
                paramName = ControlTypeConstant.CONTROL_TYPE_UNLOCK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;

                /**删除非法移动缓存*/
                redisService.del(RedisConstant.REDIS_KEY_ILLEGALITY_MOVE + machine.getMachineNO());

                /**车辆占用*/
                redisService.add(RedisConstant.REDIS_machineUnLockMap + serNO,
                        JSON.toJSONString(new MachineOccupy(machine.getAccountId(), accountUser.getAccountUserId(), machine.getMachineId(), nowTime)),
                        86400);
                break;
            case ControlType.MACHINE_CONTROL_LOCK:
                paramName = ControlTypeConstant.CONTROL_TYPE_LOCK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;

                /**车辆取消占用*/
                redisService.add(RedisConstant.REDIS_machineLockMap + serNO, machine.getMachineId().toString(), 86400);
                break;
            case ControlType.MACHINE_CONTROL_BAT_UNLOCK:
                paramName = ControlTypeConstant.MACHINE_CONTROL_BAT_UNLOCK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_BAT_LOCK:
                paramName = ControlTypeConstant.MACHINE_CONTROL_BAT_LOCK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_FIND:
                paramName = ControlTypeConstant.MACHINE_CONTROL_FIND;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_DW:
                paramName = ControlTypeConstant.CONTROL_TYPE_LOCATION;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_OPEN_TK:
                paramName = ControlTypeConstant.MACHINE_CONTROL_OPEN_TK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_CLOSE_TK:
                paramName = ControlTypeConstant.MACHINE_CONTROL_CLOSE_TK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_COERCE_HRESTE:
                paramName = ControlTypeConstant.MACHINE_CONTROL_CLOSE_HRESTE;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_COERCE_LOCK:
                paramName = ControlTypeConstant.CONTROL_TYPE_COERCE_UNLOCK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                //车辆占用
                redisService.add(RedisConstant.REDIS_machineUnLockMap + serNO,
                        JSON.toJSONString(new MachineOccupy(machine.getAccountId(), accountUser.getAccountUserId(), machine.getMachineId(), nowTime)),
                        86400);
                break;
            case ControlType.MACHINE_CONTROL_COERCE_UNLOCK:
                paramName = ControlTypeConstant.CONTROL_TYPE_COERCE_LOCK;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                /**车辆取消占用*/
                redisService.add(RedisConstant.REDIS_machineLockMap + serNO, machine.getMachineId().toString(), 86400);
                break;
            case ControlType.MACHINE_CONTROL_COERCE_PHOTOGRAPH:
                paramName = ControlUtil.buildPhotoParam(1, photograph, cameraType == null ? 0 : cameraType);
                orderType = ControlTypeConstant.CONTROL_TYPE_PHOTO;
                redisService.add(PHOTO + serNO, machine.getMachineNO(), 60);
                break;
            case ControlType.MACHINE_CONTROL_COERCE_PICTURE:
                paramName = ControlUtil.buildPhotoParam(1, photograph, cameraType == null ? 0 : cameraType);
                orderType = ControlTypeConstant.CONTROL_TYPE_UP_PIC;
                break;
            case ControlType.MACHINE_CONTROL_COERCE_HELMET:
                paramName = ControlTypeConstant.HELMET + "=" + helmetMac + ";";
                orderType = ControlTypeConstant.CONTROL_TYPE_SET;
                paramValue = helmetMac;
                break;
            case ControlType.MACHINE_CONTROL_RESTART:
                paramName = ControlTypeConstant.CONTROL_TYPE_RESTART;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                break;
            case ControlType.MACHINE_CONTROL_MANNED:
                paramName = ControlTypeConstant.MANNED_BOUND + "=" + manned + ";";
                paramValue = manned;
                orderType = ControlTypeConstant.CONTROL_TYPE_SET;
                break;
            case ControlType.MACHINE_CONTROL_PUSH_MACHINE:
                paramName = ControlTypeConstant.CONTROL_TYPE_UNSTART;
                orderType = ControlTypeConstant.CONTROL_TYPE_CONTROL;
                /**删除非法移动缓存*/
                redisService.del(RedisConstant.REDIS_KEY_ILLEGALITY_MOVE + machine.getMachineNO());

                /**车辆占用*/
                redisService.add(RedisConstant.REDIS_machineUnLockMap + serNO,
                        JSON.toJSONString(new MachineOccupy(machine.getAccountId(), accountUser.getAccountUserId(), machine.getMachineId(), nowTime)),
                        86400);

                /**推车缓存设备编号*/
                redisService.add(RedisConstant.REDIS_TER_BIKE_PUSH_MACHINE_USER_CODE + machine.getUserCode(), machine.getMachineNO());

                break;
            default:
                throw new BaseException("Machine.order.notDis");
        }
        TerMsg terMsg = new TerMsg();
        terMsg.setSn(machine.getMachineNO());
        terMsg.setSerNO(serNO);
        terMsg.setParamName(paramName);
        terMsg.setParamValue(paramValue);
        terMsg.setControlType(orderType);
        return terMsg;
    }


    private void saveOrderLog(AccountUser accountUser, String serNO, Machine machine, Integer type, String remark, String orderType, String paramName) {
        OrderLog orderLog = buildOrderLog(accountUser, serNO, machine, type, remark, orderType, paramName);
        orderLogService.insert(orderLog);
    }

    private OrderLog buildOrderLog(AccountUser accountUser, String serNO, Machine machine, Integer type, String remark, String orderType, String paramName) {
        /**远程指令日志*/
        OrderLog orderLog = new OrderLog();
        orderLog.setMachineId(machine.getMachineId());
        orderLog.setMachineNo(machine.getMachineNO());
        orderLog.setUserCode(machine.getUserCode());
        orderLog.setOpType(accountUser.getAccountUserType());
        orderLog.setOpId(accountUser.getAccountUserId());
        orderLog.setAccountUserId(accountUser.getAccountUserId());
        orderLog.setOpTime(DateTimeUtil.getNowTime());
        orderLog.setChannelType(0);
        orderLog.setBusinessType(1);
        orderLog.setOrderType(orderType);
        if (type.equals(ControlType.MACHINE_CONTROL_COERCE_PHOTOGRAPH)) {
            orderLog.setOrderType(ControlTypeConstant.CONTROL_TYPE_PHOTO);
        } else if (type.equals(ControlType.MACHINE_CONTROL_COERCE_PICTURE)) {
            orderLog.setOrderType(ControlTypeConstant.CONTROL_TYPE_UP_PIC);
        }
        orderLog.setOrderSerNo(serNO);

        // 解析指令来源
        Integer orderSource = OrderSourceUtils.getOrderSource();
        orderLog.setOrderSource(orderSource);
        orderLog.setOrderContent(paramName);
        orderLog.setRemark("运维控制:" + accountUser.getName() + remark);
        return orderLog;
    }

    @Override
    public List<TerControlResult> controlBatch(AccountUser accountUser, List<String> userCodes, Integer type,
                                               Integer mapType, Double lon, Double lat, Integer photograph, String helmetMac, Integer cameraType, Integer dispatch) {
        List<Machine> machines = machineService.getByUserCodes(userCodes);
        List<String> exitNOs = machines.stream().map(Machine::getUserCode).collect(Collectors.toList());
        List<TerControlResult> results = new ArrayList<>();
        List<String> allList = new ArrayList<>(userCodes);
        /**求差集*/
        allList.removeAll(exitNOs);
        if (CollectionUtil.isNotEmpty(allList)) {
            List<TerControlResult> notExitList = allList.stream().map(no ->
                    {
                        Machine machine = new Machine();
                        machine.setUserCode(no);
                        return TerControlResult.fail(machine, TerControlFailType.NOT_EXIST);
                    }
            ).collect(Collectors.toList());
            results.addAll(notExitList);
        }

        /**获取车辆距离限制配置*/
        Integer values = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_OPERATING_DISTANCE);
        /**获取配置，redis运维占用车辆*/
        Integer value = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_LOCK_VEHICLE);

        machines.forEach(machine -> {
            TerControlResult result = check(accountUser, machine, values, mapType, lon, lat, value, type, photograph, helmetMac, cameraType, dispatch);
            if (!result.isSuccess()) {
                results.add(result);
            }

        });

        return results;
    }

    @Override
    public void batchControl(Machine machine, List<TerControlBO> terControls) {
        if (machine == null) {
            throw new BaseException("Machine.notExist");
        }

        MachineLose machineLose = machineLoseService.getAccountIdAndUserCode(machine.getAccountId(), machine.getUserCode());
        if (machineLose != null) {
            throw new BaseException("machine.lose.exit");
        }

        AccountUser accountUser = LoginUserContextHolder.getLoginUser();

        List<TerMsg> terMessages = new ArrayList<>();
        List<OrderLog> orderLogs = new ArrayList<>();
        for (TerControlBO terControl : terControls) {
            String serNo = UUID.randomUUID().toString();
            // 创建消息对象
            TerMsg terMsg = createTerMsg(accountUser, serNo, machine, terControl.getControlType(),
                    null, null, null, null);
            // 保存指令日志
            OrderLog orderLog = buildOrderLog(accountUser, terMsg.getSerNO(), machine, terControl.getControlType(), terControl.getRemark(),
                    terMsg.getControlType(), terMsg.getParamName());

            // 放入集合
            terMessages.add(terMsg);
            orderLogs.add(orderLog);
        }

        orderLogService.insertBatch(orderLogs);

        // 发送
        sendBatchControl(machine.getMachineNO(), terMessages);
    }

    @Override
    public void batchControl(List<Machine> machines, TerControlBO terControl) {
        if (CollUtil.isEmpty(machines)) {
            throw new BaseException("Machine.notExist");
        }

        AccountUser accountUser = LoginUserContextHolder.getLoginUser();

        List<OrderLog> orderLogs = new ArrayList<>();
        List<SendControlBO> sendControls = new ArrayList<>();

        for (Machine machine : machines) {
            String serNo = UUID.randomUUID().toString();
            TerMsg terMsg = createTerMsg(accountUser, serNo, machine, terControl.getControlType(),
                    null, null, null, null);
            OrderLog orderLog = buildOrderLog(accountUser, terMsg.getSerNO(), machine, terControl.getControlType(), terControl.getRemark(),
                    terMsg.getControlType(), terMsg.getParamName());

            SendControlBO sendControlBO = SendControlBO.of(machine.getMachineNO(), terMsg);

            // 放入集合
            orderLogs.add(orderLog);
            sendControls.add(sendControlBO);
        }

        // 批量插入日志
        orderLogService.insertBatch(orderLogs);

        // 发送
        sendBatchControl(sendControls);
    }


    /**
     * 权限校验
     *
     * @param accountUser
     * @param machine
     * @return
     */
    public TerControlResult check(AccountUser accountUser, Machine machine, Integer values, Integer mapType,
                                  Double lon, Double lat, Integer value, Integer type, Integer photograph, String helmetMac, Integer cameraType, Integer dispatch) {
        /**权限判断*/
        List<Integer> agentIds = new ArrayList<>();
        for (Agent agent : accountUser.getAgents()) {
            agentIds.add(agent.getAccountId());
        }

        if (!agentIds.contains(machine.getAccountId())) {
            return TerControlResult.fail(machine, TerControlFailType.AREA_NOT_MATCH);
        }
        if (values != null && values.equals(1)) {
            Point pointUser = new Point(lon, lat);
            pointUser = calibrateService.commonCalibrate(pointUser, mapType, MapConstant.MAP_ORI);
            MachineStatus machines = machineStatusService.getByMachineId(machine.getMachineId());
            double distance = GeoUtil.getDistance(pointUser, new Point(machines.getLon(), machines.getLat()));
            AccountConfig accountConfig = dataService.getAccountConfig(machine.getAccountId());
            if (distance > accountConfig.getTerMileage()) {
                return TerControlResult.fail(machine, TerControlFailType.OPERATING_DISTANCE_LIMIT);
            }
        }

        if (value != null && value.equals(1)) {
            /**获取配置，redis运维占用车辆*/
            AccountConfig accountConfig = accountConfigService.getByAccountId(accountUser.getAccountId());
            /**运维占用最大数判断*/
            if (accountConfig != null) {
                if (accountConfig.getOccupyMax() != null && !accountConfig.getOccupyMax().equals(0)) {
                    Integer occupy = machineOccupyService.getByAccountUserId(accountUser.getAccountUserId());
                    if (occupy >= accountConfig.getOccupyMax()) {
                        return TerControlResult.fail(machine, TerControlFailType.OCCUPY_MAX);
                    }
                }
            }
        }

        /**开电池锁*/
        if (type != null && type == ControlType.MACHINE_CONTROL_BAT_UNLOCK) {
            /**获取最后状态*/
            MachineStatus machineStatus = machineStatusService.getByMachineId(machine.getMachineId());
            if (machineStatus == null) {
                throw new BaseException("Machine.notActive");
            }
            log.info("开电池锁：{}", machineStatus);

            log.info("[{}]开始批量换电", machine.getMachineNO());

            /**换电阈值配置*/
            AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
            Integer batterys = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_CHANGE_BATTERY);

            /**普通运维人员不能还低于指定阈值的车辆*/
            if (accountConfig != null && machineStatus.getSocPercent() > accountConfig.getSocLimit()) {
                log.warn("[{}]运维无效换电", machine.getMachineNO());
                if (batterys != null && batterys.equals(1)) {
                    return TerControlResult.fail(machine, TerControlFailType.UNVALID_CHANGE_BATTERY);
                }
            }

            /**是否添加*/
            boolean isAdd = true;
            if (accountConfig != null) {
                /**普通运维人员必须要在指定电量以下添加才有效*/
                if (batterys != null && batterys.equals(1)) {
                    if (machineStatus.getSocPercent() > accountConfig.getSocLimit()) {
                        isAdd = false;
                    }
                }
            }


            if (isAdd) {
                /**记录换电记录*/
                PowerChangeLog pcl = new PowerChangeLog();
                pcl.setAccountId(machine.getAccountId());
                pcl.setMachineId(machine.getMachineId());
                pcl.setUserCode(machine.getUserCode());
                pcl.setMachineNO(machine.getMachineNO());
                pcl.setUserMainId(accountUser.getAccountUserId());
                pcl.setChangeTime(DateTimeUtil.getNowTime());
                pcl.setOldSoc(machineStatus.getSocPercent());
                pcl.setMachineType(machine.getMachineType());
                pcl.setRemark("换电前电压=" + machineStatus.getBatteryEU() + ";");
                pcl.setAuditorId(1);
                pcl.setIsValid(0);
                pcl.setLat(lat);
                pcl.setLon(lon);

                /**记录运维事件轨迹*/
                try {
                    TerPosition terPosition = new TerPosition();
                    terPosition.setMachineNO(accountUser.getPhone());
                    terPosition.setLat(lat);
                    terPosition.setLon(lon);
                    terPosition.setDt(DateTimeUtil.getNowTime());
                    terPosition.setExData(HistoryExData.UW + "=" + HistoryExData.HD + ";");
                    calibrateService.calTerPosition(terPosition, mapType, SysConstant.ORIGINAL);
                    historyService.insert(terPosition);
                } catch (Exception e) {
                    log.info("错误：{}", e.getMessage(), e);
                }

                /**添加到redis*/
                redisService.add(RedisConstant.REDIS_TER_BATTERY_CHANGE + machine.getMachineNO(), JSONObject.toJSONString(pcl), 1200);


                if (flag) {
                    /** 获取换电数据，推送MQ*/
                    PowerChangeMQ powerChangeMQ = new PowerChangeMQ();
                    powerChangeMQ.setAccountId(machineStatus.getAccountId());
                    powerChangeMQ.setMachineId(machineStatus.getMachineId());
                    powerChangeMQ.setMachineNO(machineStatus.getMachineNO());
                    powerChangeMQ.setUserCode(machineStatus.getUserCode());
                    powerChangeMQ.setOldVol((double) machineStatus.getBatteryEU());
                    powerChangeMQ.setChangeTime(DateTimeUtil.StringToDateTime(DateTimeUtil.getNowTime()));
                    /** 添加到redis，推送MQ*/
                    redisService.add(RedisConstant.REDIS_TER_BATTERY_CHANGE + "MQ" + machine.getMachineNO(), JSONObject.toJSONString(powerChangeMQ), 1200);
                }

                /**添加到redismap*/
                redisService.set(RedisConstant.REDIS_TER_BIKE_BATTERY_CHANGE_MAP, machine.getMachineNO(), String.valueOf(System.currentTimeMillis()));
            }
        }

        if (dispatch.equals(1)) {
            if ((machine.getMachineType().equals(MachineTypeConstant.BIKE) && machineFunctionService.checkIsSendControl(machine.getMachineId()))
                    || machine.getMachineType().equals(MachineTypeConstant.ELECTRIC_BIKE)) {
                List<TerControlBO> terControls = new ArrayList<>();
                /**开锁*/
                TerControlBO terControl1 = new TerControlBO();
                terControl1.setControlType(ControlType.MACHINE_CONTROL_UNLOCK);
                terControl1.setRemark("调度自动开锁");
                terControls.add(terControl1);
                /**头盔锁发送强制开锁指令*/
                if (dataService.getMachineType(machine.getMachineId())) {
                    TerControlBO terControl2 = new TerControlBO();
                    terControl2.setControlType(ControlType.MACHINE_CONTROL_COERCE_LOCK);
                    terControl2.setRemark("调度自动开锁-强制开锁");
                    terControls.add(terControl2);
                }
                terControlService.batchControl(machine, terControls);
            }
        } else {
            /**生成流水号*/
            String serNO = UUID.randomUUID().toString();
            commonControl(new CommonControlParamBO(serNO, machine, type, "批量指令操作", photograph, helmetMac, null, null));
        }
        return TerControlResult.success(machine);
    }

    private void sendTempPowerMq(String machineNO, Integer accountId) {
        // 临时上电最大时间
        int tempPowerMaxTimeSecond = Optional.ofNullable(dataService.getAccountidExt(accountId + AccountConfigExtConstant.temporaryPowerTime))
                .map(AccountConfigExt::getParamValue)
                .map(Integer::parseInt)
                .filter(x -> x > 0)
                .orElse(5) * 60; // 默认值5分钟

        TempPowerMsg msg = new TempPowerMsg();
        msg.setMachineNO(machineNO);
        msg.setOrderNO(null);
        msg.setStartTime(DateUtil.now());
        msg.setDurationSec(tempPowerMaxTimeSecond);
        mqProducer.sendDataToQueue("tbit_uqbike_temp_power", JSON.toJSONString(msg));
    }
}